home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / common / logger.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-13  |  13KB  |  314 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. from __future__ import with_statement
  5.  
  6. try:
  7.     _
  8. except:
  9.     import gettext
  10.     gettext.install('Digsby')
  11.  
  12. import sys
  13. import time
  14. import re
  15. from common import profile
  16. from datetime import datetime, timedelta
  17. from util import soupify, Storage as S, tail, boolify, fromutc
  18. from path import path
  19. from digsby import iswidget
  20. from traceback import print_exc
  21. from logging import getLogger
  22. log = getLogger('logger')
  23. from common.message import Message
  24. import libxml2
  25.  
  26. def get_default_logging_dir():
  27.     lp = localprefs
  28.     import prefs
  29.     localprefs = lp()
  30.     return path(localprefs['chatlogdir']) / DEFAULT_LOG_DIR_NAME / profile.username
  31.  
  32. DEFAULT_LOG_DIR_NAME = u'Digsby Logs'
  33. LOGSIZE_PARSE_LIMIT = 15360
  34.  
  35. def buddy_path(account, buddy):
  36.     return path(account.name).joinpath(account.username, buddy.name + '_' + buddy.service)
  37.  
  38. message_timestamp_fmt = '%Y-%m-%d %H:%M:%S'
  39. message_timestamp_fmt_OLD = '%Y-%m-%d %H:%M'
  40. filename_format_re = re.compile('\\d{4}-\\d{2}-\\d{2}\\..*')
  41. message_shorttime_fmt = '%H:%M:%S %p'
  42. html_header = u'<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\n    "http://www.w3.org/TR/html4/strict.dtd">\n<HTML>\n   <HEAD>\n      <TITLE>%(title)s</TITLE>\n   <style>\n     .buddy { font-weight: bold; }\n     .buddy:after { content: ":" }\n\n     .time {\n        color: #a0a0a0;\n        font-family: monaco, courier new, monospace;\n        font-size: 75%%;\n     }\n     .time:hover { color: black; }\n\n     .outgoing { background-color: #efefef; }\n     .incoming { background-color: #ffffff; }\n   </style>\n   <script type="text/javascript">\n//<![CDATA[\n    function convert_time(datetime){\n        var dt = datetime.split(" ");\n        var date = dt[0].split("-");\n        var time = dt[1].split(":");\n        var t = new Date;\n        t.setUTCFullYear(date[0],date[1]-1,date[2]);\n        t.setUTCHours(time[0],time[1],time[2]);\n        return t.toLocaleTimeString();\n    }\n\n    function utc_to_local(){\n        var node;\n        for (var i=0; i<document.body.childNodes.length; i++){\n            node = document.body.childNodes[i];\n            if(node.nodeType == 1 && node.className.match("message")){\n                var showtime = convert_time(node.getAttribute(\'timestamp\'));\n                var newspan = \'<span class="time">(\' + showtime + \') </span>\';\n                var msgnode = node;\n                msgnode.innerHTML = newspan + msgnode.innerHTML;\n            }\n        }\n    }\n//]]>\n   </script>\n   </HEAD>\n   <BODY onload="utc_to_local()">\n'
  43. html_log_entry = '<div class="%(type)s message" auto="%(auto)s" timestamp="%(timestamp)s"><span class="buddy">%(buddy)s</span> <span class="msgcontent">%(message)s</span></div>\n'
  44.  
  45. class Logger(object):
  46.     
  47.     def __init__(self, output_dir = None, log_ims = True, log_chats = True, log_widgets = False):
  48.         self.OutputDir = output_dir
  49.         self.OutputType = 'html'
  50.         self.LogChats = log_chats
  51.         self.LogIMs = log_ims
  52.         self.LogWidgets = log_widgets
  53.  
  54.     
  55.     def on_message(self, messageobj = None, convo = None):
  56.         if not self.should_log_message(messageobj):
  57.             return None
  58.         
  59.         output = self.generate_output(messageobj)
  60.         written_size = self.write_output(output, messageobj)
  61.         
  62.         try:
  63.             buddy = messageobj.conversation.buddy
  64.         except AttributeError:
  65.             pass
  66.  
  67.         buddy.increase_log_size(written_size)
  68.  
  69.     
  70.     def should_log_message(self, messageobj):
  71.         if messageobj is None:
  72.             return False
  73.         
  74.         convo = messageobj.conversation
  75.         if convo.ischat and not (self.LogChats):
  76.             return False
  77.         elif not (convo.ischat) and not (self.LogIMs):
  78.             return False
  79.         elif not (self.LogWidgets) and iswidget(convo.buddy):
  80.             return False
  81.         elif messageobj.buddy is None:
  82.             return False
  83.         elif not messageobj.buddy.protocol.should_log(messageobj):
  84.             return False
  85.         
  86.         return True
  87.  
  88.     
  89.     def history_for(self, account, buddy):
  90.         log.info('history_for(%r, %r)', account, buddy)
  91.         files = self.logfiles_for(account, buddy)
  92.         log.info('%d %s log files found', len(files), self.OutputType)
  93.         if not files:
  94.             return iter([])
  95.         
  96.         files.sort(reverse = True)
  97.         return history_from_files(files, 'html')
  98.  
  99.     
  100.     def logsize(self, account, buddy):
  101.         return sum((lambda .0: for f in .0:
  102. f.size)(self.logfiles_for(account, buddy)))
  103.  
  104.     
  105.     def logfiles_for(self, account, buddy):
  106.         logdir = self.pathfor(account, buddy)
  107.         if not logdir.exists():
  108.             return []
  109.         
  110.         return list((lambda .0: for f in .0:
  111. if filename_format_re.match(f.name):
  112. fcontinue)(logdir.files('*.' + self.OutputType)))
  113.  
  114.     
  115.     def pathfor(self, account, buddy):
  116.         return self.OutputDir.joinpath(buddy_path(account, buddy))
  117.  
  118.     
  119.     def set_outputdir(self, val):
  120.         self.output_dir = None if val is not None else get_default_logging_dir()
  121.  
  122.     
  123.     def get_outputdir(self):
  124.         return self.output_dir
  125.  
  126.     OutputDir = property(get_outputdir, set_outputdir, doc = 'where to write logs')
  127.     
  128.     def write_output(self, output, messageobj):
  129.         convo = messageobj.conversation
  130.         proto = convo.protocol
  131.         datefilename = fromutc(messageobj.timestamp).date().isoformat()
  132.         pathelems = (buddy_path(proto, convo.buddy), datefilename)
  133.         p = path(path(self.OutputDir).joinpath(*pathelems) + '.' + self.OutputType)
  134.         if not p.parent.exists():
  135.             p.parent.makedirs()
  136.         
  137.         written_size = 0
  138.         if not p.exists():
  139.             header = globals()['generate_header_' + self.OutputType](messageobj, self.output_encoding)
  140.             written_size += len(header)
  141.             p.write_bytes(header)
  142.         
  143.         written_size += len(output)
  144.         p.write_bytes(output, append = p.exists())
  145.         return written_size
  146.  
  147.     
  148.     def generate_output(self, messageobj):
  149.         return globals()['generate_output_' + self.OutputType](messageobj, self.output_encoding)
  150.  
  151.     output_encoding = 'utf-8'
  152.  
  153.  
  154. def generate_header_html(messageobj, encoding):
  155.     c = messageobj.conversation
  156.     datefmt = messageobj.timestamp.date().isoformat()
  157.     if c.ischat:
  158.         title = 'Chat in %s on %s' % (c.name, datefmt)
  159.     else:
  160.         title = 'IM Logs with %s on %s' % (c.buddy.name, datefmt)
  161.     return (html_header % dict(title = title.encode('xml'))).encode(encoding, 'replace')
  162.  
  163.  
  164. def generate_output_html(m, encoding = 'utf-8'):
  165.     return (None % html_log_entry(dict = 'buddy' if m.buddy is not None else '', timestamp = m.timestamp.strftime(message_timestamp_fmt), message = m.message, type = m.type, auto = getattr(m, 'auto', False))).encode(encoding, 'replace')
  166.  
  167. class_buddy = {
  168.     'class': 'buddy' }
  169. class_message = {
  170.     'class': 'message' }
  171. class_msgcontent = {
  172.     'class': 'msgcontent' }
  173.  
  174. def message_divs(tag):
  175.     if tag.name == 'div':
  176.         pass
  177.     return 'message' in dict(tag.attrs).get('class', '')
  178.  
  179.  
  180. def parse_html_fast(html):
  181.     doc = libxml2.htmlParseDoc(html, 'utf-8')
  182.     
  183.     try:
  184.         divs = doc.xpathEval('//html/body/div')
  185.         messages = []
  186.         for div in divs:
  187.             message_type = div.properties.content
  188.             if 'message' not in message_type:
  189.                 continue
  190.             
  191.             type = message_type.replace('message', '').strip()
  192.             if type not in ('incoming', 'outgoing'):
  193.                 log.critical('got an unknown message type: %s', type)
  194.                 raise ValueError('unknown message type')
  195.             
  196.             props = div.properties
  197.             (auto, timestamp, buddyname, message) = (None, None, None, None)
  198.             while props:
  199.                 name = props.name
  200.                 value = props.content
  201.                 if name == 'auto':
  202.                     auto = boolify(value)
  203.                 elif name == 'timestamp':
  204.                     timestamp = parse_timestamp(value)
  205.                 
  206.                 props = props.get_next()
  207.             child = div.children
  208.             while child:
  209.                 props = child.properties
  210.                 if props is not None:
  211.                     attrs = props.content
  212.                     if attrs == 'buddy':
  213.                         buddyname = child.content.decode('utf-8')
  214.                     elif attrs == 'msgcontent':
  215.                         message = child.serialize().decode('utf-8')
  216.                     
  217.                 
  218.                 child = child.next
  219.             if all((lambda .0: for a in .0:
  220. a is not None)((auto, timestamp, buddyname, message))):
  221.                 messages.append(Message(buddy = S(name = buddyname), timestamp = timestamp, message = message, type = type, auto = auto))
  222.                 continue
  223.             raise ValueError('not all attributes could be parsed: %r' % ((auto, timestamp, buddyname, message),))
  224.         
  225.         log.info('parse_html_fast with %d bytes returning %d messages', len(html), len(messages))
  226.         return messages
  227.     finally:
  228.         doc.freeDoc()
  229.  
  230.  
  231.  
  232. def parse_html_slow(html):
  233.     soup = soupify(html, markupMassage = ((br_re, (lambda m: '<br />')),))
  234.     messages = []
  235.     strptime = datetime.strptime
  236.     for div in soup.findAll(message_divs):
  237.         
  238.         try:
  239.             buddyname = div.findAll('span', class_buddy)[0].renderContents(None)
  240.             timestamp = parse_timestamp(div['timestamp'])
  241.             message = div.findAll('span', class_msgcontent)[0].renderContents(None)
  242.             type = div['class'].replace('message', '').strip()
  243.             auto = boolify(div.get('auto', 'false'))
  244.         except Exception:
  245.             print_exc()
  246.             continue
  247.  
  248.         messages.append(Message(buddy = S(name = buddyname), timestamp = timestamp, message = message, type = type, auto = auto))
  249.     
  250.     log.info('parse_html_slow with %d bytes returning %d messages', len(html), len(messages))
  251.     return messages
  252.  
  253. USE_LIBXML2_HTML_PARSER = False
  254.  
  255. def parse_html(html, last_parsed = [
  256.     None,
  257.     None]):
  258.     digest = hash(html)
  259.     (msghash, oldmessages) = last_parsed
  260.     if msghash == digest:
  261.         return oldmessages
  262.     
  263.     if not USE_LIBXML2_HTML_PARSER or sys.platform == 'darwin':
  264.         messages = parse_html_slow(html)
  265.     else:
  266.         
  267.         try:
  268.             messages = parse_html_fast(html)
  269.         except Exception:
  270.             messages = parse_html_slow(html)
  271.             log.info('parsed slow: got %d messages', len(messages))
  272.  
  273.     last_parsed[0] = digest
  274.     last_parsed[1] = messages
  275.     return messages
  276.  
  277.  
  278. def parse_timestamp(timestamp):
  279.     
  280.     try:
  281.         return datetime.strptime(timestamp, message_timestamp_fmt)
  282.     except Exception:
  283.         return datetime.strptime(timestamp, message_timestamp_fmt_OLD)
  284.  
  285.  
  286.  
  287. def chat_path(account, room_name, *additional):
  288.     return path(account.name).joinpath(account.username, *additional)
  289.  
  290.  
  291. def history_from_files(files, logtype = 'html', encoding = 'utf-8'):
  292.     parse = globals()['parse_' + logtype]
  293.     for logfile in files:
  294.         
  295.         try:
  296.             bytes = tail(logfile, LOGSIZE_PARSE_LIMIT, encoding = encoding)
  297.         except Exception:
  298.             print_exc()
  299.             continue
  300.  
  301.         for msg in reversed(parse(bytes)):
  302.             yield msg
  303.         
  304.         if len(bytes) < logfile.size:
  305.             break
  306.             continue
  307.     
  308.  
  309. import re
  310. real_br = '<br />'
  311. br_re = re.compile('<br\\s*/?>', re.IGNORECASE)
  312.  
  313. brfix = lambda s: br_re.sub(real_br, s)
  314.